<?php

class LobbyModel extends dmq\Model
{
	public function getGames()
	{
		require_once 'php/obj/Game.php';
		require_once 'php/obj/Map.php';
		require_once 'php/obj/Player.php';
		require_once 'php/obj/Robot.php';
		require_once 'php/obj/User.php';
		
		
		$games = array();

		$stmt = $this->db->prepare( 'SELECT Games.gameId, Users.userId, Users.name, Users.email, Maps.mapId, Maps.name, Maps.width, Maps.height, Maps.tiles, Games.maxPlayers, Games.turn, Games.gameStarted, Games.gameFinished
										FROM Games
											INNER JOIN Users ON Users.userId = Games.creatorId
											INNER JOIN Maps ON Maps.mapId = Games.mapId' );
		$stmt->execute();
		$stmt->bind_result( $row_gameId, $row_creatorId, $row_creatorName, $row_creatorEmail, $row_mapId, $row_mapName, $row_mapWidth, $row_mapHeight, $row_mapTiles, $row_maxPlayers, $row_turn, $row_gameStarted, $row_gameFinished );

		$db2 = $this->db->openNew();
		while ( $stmt->fetch() )
		{
			$players = array();

			$stmt2 = $db2->prepare( 'SELECT Players.playerId, Players.cards, Players.turnFinished, Users.userId, Users.name, Users.email,
											R1.robotType, R1.spawnPosX, R1.spawnPosY, R1.posX, R1.posY, R1.direction, R1.health, R1.lives, R1.alive, R1.register1, R1.register2, R1.register3, R1.register4, R1.register5, R1.lockedRegisters, R1.powerDownStatus,
											R2.robotType, R2.spawnPosX, R2.spawnPosY, R2.posX, R2.posY, R2.direction, R2.health, R2.lives, R2.alive, R2.register1, R2.register2, R2.register3, R2.register4, R2.register5, R2.lockedRegisters, R2.powerDownStatus
										FROM Players
											INNER JOIN Users ON Users.userId = Players.userId
											INNER JOIN Robots as R1 ON R1.playerId = Players.playerId AND R1.turn = ?
											LEFT OUTER JOIN Robots as R2 ON R2.playerId = Players.playerId AND R2.turn = ( R1.turn - 1 )
										WHERE Players.gameId = ?' );
			$stmt2->bind_param( 'ii', $row_turn, $row_gameId );
			$stmt2->execute();
			$stmt2->bind_result( $row_playerId, $row_cards, $row_turnFinished, $row_userId, $row_userName, $row_userEmail,
									$row_r1_robotType, $row_r1_spawnPosX, $row_r1_spawnPosY, $row_r1_posX, $row_r1_posY, $row_r1_direction, $row_r1_health, $row_r1_lives, $row_r1_alive, $row_r1_register1, $row_r1_register2, $row_r1_register3, $row_r1_register4, $row_r1_register5, $row_r1_lockedRegisters, $row_r1_powerDownStatus,
									$row_r2_robotType, $row_r2_spawnPosX, $row_r2_spawnPosY, $row_r2_posX, $row_r2_posY, $row_r2_direction, $row_r2_health, $row_r2_lives, $row_r2_alive, $row_r2_register1, $row_r2_register2, $row_r2_register3, $row_r2_register4, $row_r2_register5, $row_r2_lockedRegisters, $row_r2_powerDownStatus );
			while ( $stmt2->fetch() )
			{
				$players[$row_playerId] = new Player( $row_playerId,
								new User( $row_userId, $row_userName, '', $row_userEmail ), $row_cards, $row_turnFinished,
								new Robot( $row_r1_robotType, $row_r1_spawnPosX, $row_r1_spawnPosY, $row_r1_posX, $row_r1_posY, $row_r1_direction, $row_r1_health, $row_r1_lives, $row_r1_alive, $row_r1_register1, $row_r1_register2, $row_r1_register3, $row_r1_register4, $row_r1_register5, $row_r1_lockedRegisters, $row_r1_powerDownStatus ),
								new Robot( $row_r2_robotType, $row_r2_spawnPosX, $row_r2_spawnPosY, $row_r2_posX, $row_r2_posY, $row_r2_direction, $row_r2_health, $row_r2_lives, $row_r2_alive, $row_r2_register1, $row_r2_register2, $row_r2_register3, $row_r2_register4, $row_r2_register5, $row_r2_lockedRegisters, $row_r2_powerDownStatus ) );
			}
			$stmt2->close();

			$games[$row_gameId] = new Game( $row_gameId, new User( $row_creatorId, $row_creatorName, '', $row_creatorEmail ), new Map( $row_mapId, $row_mapName, $row_mapWidth, $row_mapHeight, $row_mapTiles ), $row_maxPlayers, $row_turn, $row_gameStarted, $row_gameFinished, $players );
		}
		$db2->close();

		$stmt->close();

		return $games;
	}

	public function getGame( $gameId )
	{
		require_once 'php/obj/Game.php';
		require_once 'php/obj/Map.php';
		require_once 'php/obj/Player.php';
		require_once 'php/obj/Robot.php';
		require_once 'php/obj/User.php';

		$game = null;

		$stmt = $this->db->prepare( 'SELECT Games.gameId, Users.userId, Users.name, Users.email, Maps.mapId, Maps.name, Maps.width, Maps.height, Maps.tiles, Games.maxPlayers, Games.turn, Games.gameStarted, Games.gameFinished
										FROM Games
											INNER JOIN Users ON Users.userId = Games.creatorId
											INNER JOIN Maps ON Maps.mapId = Games.mapId
										WHERE Games.gameId = ?' );
		$stmt->bind_param( 'i', $gameId );
		$stmt->execute();
		$stmt->bind_result( $row_gameId, $row_creatorId, $row_creatorName, $row_creatorEmail, $row_mapId, $row_mapName, $row_mapWidth, $row_mapHeight, $row_mapTiles, $row_maxPlayers, $row_turn, $row_gameStarted, $row_gameFinished );

		$db2 = $this->db->openNew();
		$stmt->fetch();

		$players = array();

		$stmt2 = $db2->prepare( 'SELECT Players.playerId, Players.cards, Players.turnFinished, Users.userId, Users.name, Users.email,
										R1.robotType, R1.spawnPosX, R1.spawnPosY, R1.posX, R1.posY, R1.direction, R1.health, R1.lives, R1.alive, R1.register1, R1.register2, R1.register3, R1.register4, R1.register5, R1.lockedRegisters, R1.powerDownStatus,
										R2.robotType, R2.spawnPosX, R2.spawnPosY, R2.posX, R2.posY, R2.direction, R2.health, R2.lives, R2.alive, R2.register1, R2.register2, R2.register3, R2.register4, R2.register5, R2.lockedRegisters, R2.powerDownStatus
									FROM Players
										INNER JOIN Users ON Users.userId = Players.userId
										INNER JOIN Robots as R1 ON R1.playerId = Players.playerId AND R1.turn = ?
										LEFT OUTER JOIN Robots as R2 ON R2.playerId = Players.playerId AND R2.turn = ( R1.turn - 1 )
									WHERE Players.gameId = ?' );
		$stmt2->bind_param( 'ii', $row_turn, $row_gameId );
		$stmt2->execute();
		$stmt2->bind_result( $row_playerId, $row_cards, $row_turnFinished, $row_userId, $row_userName, $row_userEmail,
								$row_r1_robotType, $row_r1_spawnPosX, $row_r1_spawnPosY, $row_r1_posX, $row_r1_posY, $row_r1_direction, $row_r1_health, $row_r1_lives, $row_r1_alive, $row_r1_register1, $row_r1_register2, $row_r1_register3, $row_r1_register4, $row_r1_register5, $row_r1_lockedRegisters, $row_r1_powerDownStatus,
								$row_r2_robotType, $row_r2_spawnPosX, $row_r2_spawnPosY, $row_r2_posX, $row_r2_posY, $row_r2_direction, $row_r2_health, $row_r2_lives, $row_r2_alive, $row_r2_register1, $row_r2_register2, $row_r2_register3, $row_r2_register4, $row_r2_register5, $row_r2_lockedRegisters, $row_r2_powerDownStatus );
		while ( $stmt2->fetch() )
		{
			$players[$row_playerId] = new Player( $row_playerId,
							new User( $row_userId, $row_userName, '', $row_userEmail ), $row_cards, $row_turnFinished,
							new Robot( $row_r1_robotType, $row_r1_spawnPosX, $row_r1_spawnPosY, $row_r1_posX, $row_r1_posY, $row_r1_direction, $row_r1_health, $row_r1_lives, $row_r1_alive, $row_r1_register1, $row_r1_register2, $row_r1_register3, $row_r1_register4, $row_r1_register5, $row_r1_lockedRegisters, $row_r1_powerDownStatus ),
							new Robot( $row_r2_robotType, $row_r2_spawnPosX, $row_r2_spawnPosY, $row_r2_posX, $row_r2_posY, $row_r2_direction, $row_r2_health, $row_r2_lives, $row_r2_alive, $row_r2_register1, $row_r2_register2, $row_r2_register3, $row_r2_register4, $row_r2_register5, $row_r2_lockedRegisters, $row_r2_powerDownStatus ) );
		}
		$stmt2->close();
		$db2->close();

		$game = new Game( $row_gameId, new User( $row_creatorId, $row_creatorName, '', $row_creatorEmail ), new Map( $row_mapId, $row_mapName, $row_mapWidth, $row_mapHeight, $row_mapTiles ), $row_maxPlayers, $row_turn, $row_gameStarted, $row_gameFinished, $players );

		$stmt->close();

		return $game;
	}

	public function filterGames( $games, $filter )
	{
		 $filteredGames = array();
		 foreach ( $games as $gameID => $game )
		 {
		 	$passFilter = true;


		 	if ( array_key_exists( 'creator', $filter ) && $game->Creator()->ID() != $filter['creator'] )
		 	{
		 		$passFilter = false;
		 	}

		 	if ( array_key_exists( 'player', $filter ) )
		 	{
		 		$hasPlayer = false;
		 		foreach ( $game->Players() as $player )
		 		{
		 			if ( $player->User()->ID() == $filter['player'] )
		 			{
		 				$hasPlayer = true;
		 			}
		 		}

		 		if ( !$hasPlayer )
		 		{
		 			$passFilter = false;
		 		}
		 	}

		 	if ( $passFilter )
		 	{
		 		$filteredGames[$gameID] = $game;
		 	}
		 }

		 return $filteredGames;
	}

	public function getMaps()
	{
		require_once 'php/obj/Map.php';
		
		$maps = array();

		$stmt = $this->db->prepare( 'SELECT mapId, name, width, height, tiles FROM Maps' );
		$stmt->execute();
		$stmt->bind_result( $row_mapId, $row_name, $row_width, $row_height, $row_tiles );
		while ( $stmt->fetch() )
		{
			$maps[] = new Map( $row_mapId, $row_name, $row_width, $row_height, $row_tiles );
		}
		$stmt->close();
		
		return $maps;
	}

	public function createGame( $creatorId, $maxPlayers, $mapId )
	{
		$this->db->startTransaction();

		// Add Game.
		$turn = 1;
		$gameStarted = 0;
		$gameFinished = 0;

		$stmt = $this->db->prepare( 'INSERT INTO Games ( creatorId, mapId, maxPlayers, turn, gameStarted, gameFinished ) VALUES( ?, ?, ?, ?, ?, ? )' );
		$stmt->bind_param( 'iiiiii', $creatorId, $mapId, $maxPlayers, $turn, $gameStarted, $gameFinished );
		$this->db->execute( $stmt );
		$stmt->close();

		// Add the creator as a Participant.
		$gameId = $this->db->lastInsertID();
		$cards = '';
		$turnFinished = 0;

		$stmt = $this->db->prepare( 'INSERT INTO Players ( userId, gameId, cards, turnFinished ) VALUES ( ?, ?, ?, ? )' );
		$stmt->bind_param( 'iisi', $creatorId, $gameId, $cards, $turnFinished );
		$this->db->execute( $stmt );
		$stmt->close();

		// Add the creators Robot.
		$playerId = $this->db->lastInsertID();
		$robotType = 0;
		$spawnPosX = 0;
		$spawnPosY = 0;
		$posX = 0;
		$posY = 0;
		$direction = 2;
		$health = 10;
		$lives = 5;
		$alive = 1;
		$powerDownStatus = 0;

		$stmt = $this->db->prepare( 'INSERT INTO Robots ( playerId, turn, robotType, spawnPosX, spawnPosY, posX, posY, direction, health, lives, alive, powerDownStatus )
															VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )' );
		$stmt->bind_param( 'iiiiiiiiiiii', $playerId, $turn, $robotType, $spawnPosX, $spawnPosY, $posX, $posY, $direction, $health, $lives, $alive, $powerDownStatus );
		$this->db->execute( $stmt );
		$stmt->close();

		if ( !$this->db->endTransaction() )
		{
			throw new Exception( 'Could not create game.' );
		}
	}

	public function joinGame( $game, $userId )
	{
		$gameId = $game->id();

		// Check if the game has already started.
		$stmt = $this->db->prepare( 'SELECT gameStarted FROM Games WHERE gameId = ?' );
		$stmt->bind_param( 'i', $gameId );
		$stmt->execute();
		$stmt->bind_result( $row_started );
		$stmt->fetch();
		if ( $row_started )
		{
			$stmt->close();
			throw new Exception( 'Can not join a game in progress.' );
		}
		$stmt->close();

		// Check if the game is already full.
		if ( $game->maxPlayers() <= count( $game->players() ) )
		{
			throw new Exception( 'Cannot join a full game.' );
		}

		// Check if the player has already joined the game.
		foreach ( $game->players() as $player )
		{
			if ( $player->user()->id() == $userId )
			{
				throw new Exception( 'You have already joined this game.' );
			} 
		}


		$this->db->startTransaction();


		// Add player to the game.
		$cards = '';
		$turnFinished = 0;

		$stmt = $this->db->prepare( 'INSERT INTO Players ( userId, gameId, cards, turnFinished ) VALUES ( ?, ?, ?, ? )' );
		$stmt->bind_param( 'iisi', $userId, $gameId, $cards, $turnFinished );
		$this->db->execute( $stmt );
		$stmt->close();

		// Add players robot to the game.
		$playerId = $this->db->lastInsertID();
		$robotType = 0;
		$spawnPosX = 0;
		$spawnPosY = 0;
		$posX = 0;
		$posY = 0;
		$direction = 2;
		$health = 10;
		$lives = 5;
		$alive = 1;
		$powerDownStatus = 0;
		$turn = $game->turn();

		$stmt = $this->db->prepare( 'INSERT INTO Robots ( playerId, turn, robotType, spawnPosX, spawnPosY, posX, posY, direction, health, lives, alive, powerDownStatus )
															VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )' );
		$stmt->bind_param( 'iiiiiiiiiiii', $playerId, $turn, $robotType, $spawnPosX, $spawnPosY, $posX, $posY, $direction, $health, $lives, $alive, $powerDownStatus );
		$this->db->execute( $stmt );
		$stmt->close();

		if ( !$this->db->endTransaction() )
		{
			throw new Exception( 'Could not join the game.' );
		}
	}

	public function leaveGame( $game, $playerId, $userId )
	{
		$gameId = $game->id();
		
		// Check if the game has already started.
		$stmt = $this->db->prepare( 'SELECT gameStarted FROM Games WHERE gameId = ?' );
		$stmt->bind_param( 'i', $gameId );
		$stmt->execute();
		$stmt->bind_result( $row_started );
		$stmt->fetch();
		if ( $row_started )
		{
			$stmt->close();
			throw new Exception( 'Can not leave a game in progress.' );
		}
		$stmt->close();


		// Check if the user is actually the player he is trying to leave with.
		$players = $game->players();
		if ( !array_key_exists( $playerId, $players) || $players[$playerId]->user()->id() != $userId )
		{
			throw new Exception( 'You are not the player trying to leave.' );
		}

		// Remove player and it's robot from the game.
		$this->db->startTransaction();

		$stmt = $this->db->prepare( 'DELETE FROM Players WHERE playerId = ?' );
		$stmt->bind_param( 'i', $playerId );
		$this->db->execute( $stmt );
		$stmt->close();

		$stmt = $this->db->prepare( 'DELETE FROM Robots WHERE playerId = ?' );
		$stmt->bind_param( 'i', $playerId );
		$this->db->execute( $stmt );
		$stmt->close();

		if ( !$this->db->endTransaction() )
		{
			throw new Exception( 'Could not leave the game at the moment.' );
		}
	}

	public function removePlayer( $game, $playerId, $userId )
	{
		$gameId = $game->id();
		
		// Check if the game has already started.
		$stmt = $this->db->prepare( 'SELECT gameStarted FROM Games WHERE gameId = ?' );
		$stmt->bind_param( 'i', $gameId );
		$stmt->execute();
		$stmt->bind_result( $row_started );
		$stmt->fetch();
		if ( $row_started )
		{
			$stmt->close();
			throw new Exception( 'Can not remove players from a game in progress.' );
		}
		$stmt->close();

		// Check if the user is not the game creator.
		if ( $game->creator()->id() != $userId )
		{
			throw new Exception( 'You are not the game operator.' );
		}

		// Check if the player really is a player of the game.
		$players = $game->players();
		if ( !array_key_exists( $playerId, $players) )
		{
			throw new Exception( 'The player you are trying to remove is not in the game.' );
		}

		// Remove the game and all it's players and their robots, if it is the game creator being removed.
		if ( $players[$playerId]->user()->id() == $userId )
		{
			$this->db->startTransaction();

			foreach ( $players as $player )
			{
				$row_playerId = $player->id();

				$stmt = $this->db->prepare( 'DELETE FROM Robots WHERE playerId = ?' );
				$stmt->bind_param( 'i', $row_playerId );
				$this->db->execute( $stmt );
				$stmt->close();
			}

			$stmt = $this->db->prepare( 'DELETE FROM Players WHERE gameId = ?' );
			$stmt->bind_param( 'i', $gameId );
			$this->db->execute( $stmt );
			$stmt->close();	

			$stmt = $this->db->prepare( 'DELETE FROM Games WHERE gameId = ? AND creatorId = ?' );
			$stmt->bind_param( 'ii', $gameId, $userId );
			$this->db->execute( $stmt );
			$stmt->close();

			if ( !$this->db->endTransaction() )
			{
				throw new Exception( 'Could not delete the game.' );
			}
		}
		else // Just remove the player if it's not the operator.
		{
			$this->db->startTransaction();

			$stmt = $this->db->prepare( 'DELETE FROM Robots WHERE playerId = ?' );
			$stmt->bind_param( 'i', $playerId );
			$this->db->execute( $stmt );
			$stmt->close();

			$stmt = $this->db->prepare( 'DELETE FROM Players WHERE playerId = ?' );
			$stmt->bind_param( 'i', $playerId );
			$this->db->execute( $stmt );
			$stmt->close();	

			if ( !$this->db->endTransaction() )
			{
				throw new Exception( 'Could not remove the player.' );
			}
		}
	}

	public function startGame( $game, $userId )
	{
		$map = '';
		$gameId = $game->id();
		
		// Check if the game has already started.
		$stmt = $this->db->prepare( 'SELECT gameStarted FROM Games WHERE gameId = ?' );
		$stmt->bind_param( 'i', $gameId );
		$stmt->execute();
		$stmt->bind_result( $row_started );
		$stmt->fetch();
		if ( $row_started )
		{
			$stmt->close();
			throw new Exception( 'The game is already started.' );
		}
		$stmt->close();

		// Check if the user is not the game creator.
		if ( $game->creator()->id() != $userId )
		{
			throw new Exception( 'You are not the game operator.' );
		}

		//Get the map
		require_once "php/game/engine.php";
		
		//Create the map to get spawnposes
		$map = \game\Map::DecodeMapString( $game->map()->width(), $game->map()->height(), $game->map()->tiles() );

		// Shuffle the startpositions.
		shuffle( $map->m_startpositions );

		$this->db->startTransaction();
		
		$counter = 0;

		// Update secondplayer to ID 1 e.t.c.
		foreach ( $game->players() as $playerId => $player )
		{
			//$player->robot()->setPos( $f_map->m_startpositions[$counter]->m_x, $f_map->m_startpositions[$counter]->m_y );
			
			$posX = $map->m_startpositions[$counter]->m_x;
			$posY = $map->m_startpositions[$counter]->m_y;		
			
			if (!is_int($posX) || !is_int($posY) )
			{
				throw new Exception("Could not get pos from startpositions.");				
			}			
			
			$stmt = $this->db->prepare( 'UPDATE Robots SET spawnPosX = ?, spawnPosY = ?, posX = ?, posY = ?, robotType = ? WHERE playerId = ? AND turn = 1' );
			$stmt->bind_param( 'iiiiii', $posX, $posY, $posX, $posY, $counter , $playerId );
			$this->db->execute( $stmt );
			$stmt->close();

			$counter++;
		}

		$stmt = $this->db->prepare( 'UPDATE Games SET gameStarted = 1 WHERE gameId = ? AND creatorId = ?' );
		$stmt->bind_param( 'ii', $gameId, $userId );
		$this->db->execute( $stmt );
		$stmt->close();

		require_once 'php/lib/CardDealer.php';
		$dealer = new CardDealer();

		foreach ( $dealer->dealCards( $game->players() ) as $playerId => $playerCards )
		{
			$stmt = $this->db->prepare( 'UPDATE Players SET cards = ? WHERE playerId = ?' );
			$stmt->bind_param( 'si', $playerCards, $playerId );
			$this->db->execute( $stmt );
			$stmt->close();
		}

		if ( !$this->db->endTransaction() )
		{
			throw new Exception( 'Could not start the game.' );
		}
	}
	public function push($channel, $event, $message)
	{
		require_once 'BeaconPush/classes/beaconpush.php';
		$beaconpush = new BeaconPush();
		$beaconpush->add_channel($channel);
		$beaconpush->send_to_channel($channel, $event, $message);
	}
}
